/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Parser;

import lowlevel.BasicBlock;
import lowlevel.Function;
import lowlevel.Operand;
import lowlevel.Operation;

/**
 *
 * @author maxmuhlenkamp
 */
public class SelectionStmt extends Statement {
    Expression expr;
    Statement stmt;
    Statement elseStmt;
    
    public SelectionStmt(Expression expr, Statement stmt, Statement optionalElse){
        this.expr = expr;
        this.stmt = stmt;
        this.elseStmt = optionalElse;
    }

    @Override
    void print(String indent) {
        CMinusParser.oFile.println(indent + "SelectionStmt");
        
        CMinusParser.oFile.println(indent + "  if");
        expr.print(indent + "    ");
        
        CMinusParser.oFile.println(indent + "  then");
        stmt.print(indent + "    ");
        
        if(elseStmt != null){
            CMinusParser.oFile.println(indent + "  else");
            elseStmt.print(indent + "    ");
        }
        CMinusParser.oFile.flush();
    }

    @Override
    void genLLCode(Function func) {
        boolean includeElse = (elseStmt != null);
        
        BasicBlock thenBlock = new BasicBlock(func);
        BasicBlock postBlock = new BasicBlock(func);
        BasicBlock elseBlock = null;
        
        if(includeElse){
            elseBlock = new BasicBlock(func);
        }
        
        // evaluate condition statement
        expr.genLLCode(func);

        // generate branch operation and operands
        Operation branch = new Operation(Operation.OperationType.BEQ, func.getCurrBlock());
        Operand src0 = new Operand(Operand.OperandType.REGISTER, expr.getRegNum());
        Operand src1 = new Operand(Operand.OperandType.INTEGER, 0);
        Operand src2 = null;

        if(includeElse){
            src2 = new Operand(Operand.OperandType.BLOCK, elseBlock.getBlockNum());
        }
        else{
            src2 = new Operand(Operand.OperandType.BLOCK, postBlock.getBlockNum());
        }
        // add the branch operation
        branch.setSrcOperand(0, src0);
        branch.setSrcOperand(1, src1);
        branch.setSrcOperand(2, src2);
        func.getCurrBlock().appendOper(branch);
        
        // append then block and set it to current block
        func.appendToCurrentBlock(thenBlock);
        func.setCurrBlock(thenBlock);
        
        // gen code on then statement
        stmt.genLLCode(func);
        
        // append post block
        func.appendToCurrentBlock(postBlock);
        
        if(includeElse){
            func.setCurrBlock(elseBlock);
            
            // gen code on else
            elseStmt.genLLCode(func);
            
            // create jump to post block
            Operation jmpPost = new Operation(Operation.OperationType.JMP, func.getCurrBlock());
            Operand target = new Operand(Operand.OperandType.BLOCK, postBlock.getBlockNum());
            
            jmpPost.setSrcOperand(0, target);
            func.getCurrBlock().appendOper(jmpPost);
            
            // append else to unconnected chain
            func.appendUnconnectedBlock(elseBlock);
        }
        
        func.setCurrBlock(postBlock);
    }
}
